#pragma once 
#include<iostream>
#include<queue>
#include<pthread.h>


template<class T>
class BlockQueue
{
    private:
        std::queue<T> _block_queue;//阻塞队列
        int _capacity;//容量
        pthread_mutex_t _mutex;//保护阻塞队列-互斥锁
        pthread_cond_t _consumer_cond;//消费者条件变量
        pthread_cond_t _productor_cond;//生产这条件变量

        int _productor_wait_num;
        int _consumer_wait_num;
    private:
        bool IsFull()
        {
            return _block_queue.size() == _capacity;
        }
        bool IsEmpty()
        {
            return _block_queue.empty();
        }
    public:
        BlockQueue(int cap)
        :_capacity(cap)
        {
            _consumer_wait_num = 0;
            _productor_wait_num = 0;
            pthread_mutex_init(&_mutex,nullptr);
            pthread_cond_init(&_consumer_cond,nullptr);
            pthread_cond_init(&_productor_cond,nullptr);
        }
        void Enqueue(T& in) //生产者专用接口
        {
            pthread_mutex_lock(&_mutex);
            while(IsFull())//防止一次唤醒多个线程，只有一个位置需要生产，出现bug，每个线程被唤醒后要重新判断是否有位置生产数据
            {
                // 生产线程去等待,是在临界区中休眠的！你现在还持有锁呢！！！
                // 1. pthread_cond_wait调用是: a. 让调用进程等待 b. 自动释放曾经持有的_mutex锁
                // 必须重新竞争_mutex锁，竞争成功，方可返回！！！为什么？
                // 之前：安全
                _productor_wait_num++;
                pthread_cond_wait(&_productor_cond,&_mutex);//解锁--入等待队列-唤醒-出等待队列--加锁
                _productor_wait_num--;
                //之后安全
            }
            std::cout<<in<<std::endl;
            _block_queue.push(in);
            //通知消费者
            if(_consumer_wait_num > 0)
                pthread_cond_signal(&_consumer_cond);   
            pthread_mutex_unlock(&_mutex);
        }
        void Pop(T* out)//消费者专用接口
        {
            pthread_mutex_lock(&_mutex);
            while(IsEmpty())
            {
                // 消费线程去等待,是在临界区中休眠的！你现在还持有锁呢！！！
                // 1. pthread_cond_wait调用是: a. 让调用进程等待 b. 自动释放曾经持有的_mutex锁
                _consumer_wait_num++;
                pthread_cond_wait(&_consumer_cond,&_mutex);
                _consumer_wait_num--;
            }
            //消费
            *out = _block_queue.front();
            _block_queue.pop();
            //通知生产者生产
            if(_productor_wait_num > 0)
                pthread_cond_signal(&_productor_cond);
            pthread_mutex_unlock(&_mutex);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_consumer_cond);
            pthread_cond_destroy(&_productor_cond);
        }
};